//
//  ResponseError.swift
//  RoBiliBiliHotVideo
//
//  Created by 罗万能 on 2025/7/5.
//


import Foundation

enum ResponseError: Error, LocalizedError {
    case invalidData
    case invalidURL
    case networkError(Error)
    case decodingError(Error)
    case apiError(code: Int, message: String)
    
    var errorDescription: String? {
        switch self {
        case .invalidData:
            return "无效的数据格式"
        case .invalidURL:
            return "无效的URL"
        case .networkError(let error):
            return "网络错误: \(error.localizedDescription)"
        case .decodingError(let error):
            return "数据解码错误: \(error.localizedDescription)"
        case .apiError(code: let code, message: let message):
            return "API错误: 代码 \(code), 消息: \(message)"
        }
    }
}

public struct SearchHotResponse: Codable,Sendable {
    
    public static func createModel(from data: Data) -> SearchHotResponse?{
        let jsonDecoder = JSONDecoder()
        do{
            let responseModel = try jsonDecoder.decode(SearchHotResponse.self, from: data)
                return responseModel
            
        }catch{
            print(error)
            return nil
//            fatalError("parsing error")
        }
    }
    let code: Int
    let message: String
    let ttl: Int
    let data: ResponseData?
}

public struct ResponseData: Codable,Sendable {
    let note: String
    let list: [VideoItem]
}

public struct Rights: Codable,Sendable {
    let bp: Int
    let elec: Int
    let download: Int
    let movie: Int
    let pay: Int
    let hd5: Int
    let no_reprint: Int
    let autoplay: Int
    let ugc_pay: Int
    let is_cooperation: Int
    let ugc_pay_preview: Int
    let no_background: Int
    let arc_pay: Int
    let pay_free_watch: Int
    
    init() {
        bp = 0
        elec = 0
        download = 0
        movie = 0
        pay = 0
        hd5 = 0
        no_reprint = 0
        autoplay = 0
        ugc_pay = 0
        is_cooperation = 0
        ugc_pay_preview = 0
        no_background = 0
        arc_pay = 0
        pay_free_watch = 0
    }
    
    enum CodingKeys: String, CodingKey {
        case bp, elec, download, movie, pay, hd5, no_reprint, autoplay, ugc_pay, is_cooperation, ugc_pay_preview, no_background, arc_pay, pay_free_watch
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        bp = try container.decodeIfPresent(Int.self, forKey: .bp) ?? 0
        elec = try container.decodeIfPresent(Int.self, forKey: .elec) ?? 0
        download = try container.decodeIfPresent(Int.self, forKey: .download) ?? 0
        movie = try container.decodeIfPresent(Int.self, forKey: .movie) ?? 0
        pay = try container.decodeIfPresent(Int.self, forKey: .pay) ?? 0
        hd5 = try container.decodeIfPresent(Int.self, forKey: .hd5) ?? 0
        no_reprint = try container.decodeIfPresent(Int.self, forKey: .no_reprint) ?? 0
        autoplay = try container.decodeIfPresent(Int.self, forKey: .autoplay) ?? 0
        ugc_pay = try container.decodeIfPresent(Int.self, forKey: .ugc_pay) ?? 0
        is_cooperation = try container.decodeIfPresent(Int.self, forKey: .is_cooperation) ?? 0
        ugc_pay_preview = try container.decodeIfPresent(Int.self, forKey: .ugc_pay_preview) ?? 0
        no_background = try container.decodeIfPresent(Int.self, forKey: .no_background) ?? 0
        arc_pay = try container.decodeIfPresent(Int.self, forKey: .arc_pay) ?? 0
        pay_free_watch = try container.decodeIfPresent(Int.self, forKey: .pay_free_watch) ?? 0
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(bp, forKey: .bp)
        try container.encode(elec, forKey: .elec)
        try container.encode(download, forKey: .download)
        try container.encode(movie, forKey: .movie)
        try container.encode(pay, forKey: .pay)
        try container.encode(hd5, forKey: .hd5)
        try container.encode(no_reprint, forKey: .no_reprint)
        try container.encode(autoplay, forKey: .autoplay)
        try container.encode(ugc_pay, forKey: .ugc_pay)
        try container.encode(is_cooperation, forKey: .is_cooperation)
        try container.encode(ugc_pay_preview, forKey: .ugc_pay_preview)
        try container.encode(no_background, forKey: .no_background)
        try container.encode(arc_pay, forKey: .arc_pay)
        try container.encode(pay_free_watch, forKey: .pay_free_watch)
    }
}

struct Owner: Codable {
    let mid: Int
    let name: String
    let face: String
    
    init() {
        mid = 0
        name = ""
        face = ""
    }
    
    enum CodingKeys: String, CodingKey {
        case mid, name, face
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        mid = try container.decodeIfPresent(Int.self, forKey: .mid) ?? 0
        name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
        face = try container.decodeIfPresent(String.self, forKey: .face) ?? ""
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mid, forKey: .mid)
        try container.encode(name, forKey: .name)
        try container.encode(face, forKey: .face)
    }
}

struct Stat: Codable {
    let aid: Int
    let view: Int
    let danmaku: Int
    let reply: Int
    let favorite: Int
    let coin: Int
    let share: Int
    let now_rank: Int
    let his_rank: Int
    let like: Int
    let dislike: Int
    let vt: Int
    let vv: Int
    let fav_g: Int
    let like_g: Int
    
    init() {
        aid = 0
        view = 0
        danmaku = 0
        reply = 0
        favorite = 0
        coin = 0
        share = 0
        now_rank = 0
        his_rank = 0
        like = 0
        dislike = 0
        vt = 0
        vv = 0
        fav_g = 0
        like_g = 0
    }
    
    enum CodingKeys: String, CodingKey {
        case aid, view, danmaku, reply, favorite, coin, share, now_rank, his_rank, like, dislike, vt, vv, fav_g, like_g
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        aid = try container.decodeIfPresent(Int.self, forKey: .aid) ?? 0
        view = try container.decodeIfPresent(Int.self, forKey: .view) ?? 0
        danmaku = try container.decodeIfPresent(Int.self, forKey: .danmaku) ?? 0
        reply = try container.decodeIfPresent(Int.self, forKey: .reply) ?? 0
        favorite = try container.decodeIfPresent(Int.self, forKey: .favorite) ?? 0
        coin = try container.decodeIfPresent(Int.self, forKey: .coin) ?? 0
        share = try container.decodeIfPresent(Int.self, forKey: .share) ?? 0
        now_rank = try container.decodeIfPresent(Int.self, forKey: .now_rank) ?? 0
        his_rank = try container.decodeIfPresent(Int.self, forKey: .his_rank) ?? 0
        like = try container.decodeIfPresent(Int.self, forKey: .like) ?? 0
        dislike = try container.decodeIfPresent(Int.self, forKey: .dislike) ?? 0
        vt = try container.decodeIfPresent(Int.self, forKey: .vt) ?? 0
        vv = try container.decodeIfPresent(Int.self, forKey: .vv) ?? 0
        fav_g = try container.decodeIfPresent(Int.self, forKey: .fav_g) ?? 0
        like_g = try container.decodeIfPresent(Int.self, forKey: .like_g) ?? 0
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(aid, forKey: .aid)
        try container.encode(view, forKey: .view)
        try container.encode(danmaku, forKey: .danmaku)
        try container.encode(reply, forKey: .reply)
        try container.encode(favorite, forKey: .favorite)
        try container.encode(coin, forKey: .coin)
        try container.encode(share, forKey: .share)
        try container.encode(now_rank, forKey: .now_rank)
        try container.encode(his_rank, forKey: .his_rank)
        try container.encode(like, forKey: .like)
        try container.encode(dislike, forKey: .dislike)
        try container.encode(vt, forKey: .vt)
        try container.encode(vv, forKey: .vv)
        try container.encode(fav_g, forKey: .fav_g)
        try container.encode(like_g, forKey: .like_g)
    }
}

struct Dimension: Codable {
    let width: Int
    let height: Int
    let rotate: Int
    
    init(width: Int, height: Int, rotate: Int) {
        self.width = width
        self.height = height
        self.rotate = rotate
    }
    
    enum CodingKeys: String, CodingKey {
        case width, height, rotate
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        width = try container.decodeIfPresent(Int.self, forKey: .width) ?? 0
        height = try container.decodeIfPresent(Int.self, forKey: .height) ?? 0
        rotate = try container.decodeIfPresent(Int.self, forKey: .rotate) ?? 0
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(width, forKey: .width)
        try container.encode(height, forKey: .height)
        try container.encode(rotate, forKey: .rotate)
    }
}

struct OgvInfo: Codable {
    let season_id: Int
    let episode_id: Int
    let title: String
    
    enum CodingKeys: String, CodingKey {
        case season_id, episode_id, title
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        season_id = try container.decodeIfPresent(Int.self, forKey: .season_id) ?? 0
        episode_id = try container.decodeIfPresent(Int.self, forKey: .episode_id) ?? 0
        title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(season_id, forKey: .season_id)
        try container.encode(episode_id, forKey: .episode_id)
        try container.encode(title, forKey: .title)
    }
}

struct RcmdReason: Codable {
    let content: String
    let corner_mark: Int
    let reason_type: Int
    
    enum CodingKeys: String, CodingKey {
        case content, corner_mark, reason_type
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        content = try container.decodeIfPresent(String.self, forKey: .content) ?? ""
        corner_mark = try container.decodeIfPresent(Int.self, forKey: .corner_mark) ?? 0
        reason_type = try container.decodeIfPresent(Int.self, forKey: .reason_type) ?? 0
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(content, forKey: .content)
        try container.encode(corner_mark, forKey: .corner_mark)
        try container.encode(reason_type, forKey: .reason_type)
    }
}

public struct VideoItem: Codable,Sendable {
    let aid: Int
    let videos: Int?
    
    enum CodingKeys: String, CodingKey {
        case aid, videos, tid, tname, copyright, pic, title, pubdate, ctime, desc, state, duration, rights, owner, stat, dynamic, dimension, first_frame, pub_location, short_link, short_link_v2, bvid, season_type, is_ogv, ogv_info, rcmd_reason, up_from_v2, up_tags, disable_show_up_info, is_live_playback, playurl, credit, cid, season_id, attribute_v2, attribute, mission_id, enable_vt, others, score, pid_name_v2, pid_v2, tnamev2, tidv2, cover43
    }
    
    let tid: Int
    let tname: String
    let copyright: Int
    let pic: String
    let title: String
    let pubdate: Int
    let ctime: Int
    let desc: String
    let state: Int
    let duration: Int
    let rights: Rights
    let owner: Owner
    let stat: Stat
    let dynamic: String
    let cid: Int
    let dimension: Dimension
    let short_link_v2: String
    let first_frame: String
    let pub_location: String
    let cover43: String
    let tidv2: Int
    let tnamev2: String
    let pid_v2: Int
    let pid_name_v2: String
    let bvid: String
    let ogv_info: OgvInfo?
    let rcmd_reason: RcmdReason?
//    let up_tags: [String]?
    let score: Int
    let others: [VideoItem]?
    let enable_vt: Int
    let mission_id: Int?
    let attribute: Int?
    let attribute_v2: Int?
    let season_id: Int?
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 处理aid字段可能为Int或String的情况
        if let aidInt = try? container.decode(Int.self, forKey: .aid) {
            aid = aidInt
        } else if let aidString = try? container.decode(String.self, forKey: .aid), let aidInt = Int(aidString) {
            aid = aidInt
        } else {
            throw DecodingError.dataCorruptedError(forKey: .aid, in: container, debugDescription: "aid must be Int or String convertible to Int")
        }
        
        videos = try container.decodeIfPresent(Int.self, forKey: .videos)
        tid = try container.decodeIfPresent(Int.self, forKey: .tid) ?? 0
        tname = try container.decodeIfPresent(String.self, forKey: .tname) ?? ""
        copyright = try container.decodeIfPresent(Int.self, forKey: .copyright) ?? 0
        pic = try container.decodeIfPresent(String.self, forKey: .pic) ?? ""
        title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
        pubdate = try container.decodeIfPresent(Int.self, forKey: .pubdate) ?? 0
        ctime = try container.decodeIfPresent(Int.self, forKey: .ctime) ?? 0
        desc = try container.decodeIfPresent(String.self, forKey: .desc) ?? ""
        state = try container.decodeIfPresent(Int.self, forKey: .state) ?? 0
        duration = try container.decodeIfPresent(Int.self, forKey: .duration) ?? 0
        rights = try container.decodeIfPresent(Rights.self, forKey: .rights) ?? Rights()
        owner = try container.decodeIfPresent(Owner.self, forKey: .owner) ?? Owner()
        stat = try container.decodeIfPresent(Stat.self, forKey: .stat) ?? Stat()
        dimension = try container.decodeIfPresent(Dimension.self, forKey: .dimension) ?? Dimension(width: 0, height: 0, rotate: 0)
        short_link_v2 = try container.decodeIfPresent(String.self, forKey: .short_link_v2) ?? ""
        first_frame = try container.decodeIfPresent(String.self, forKey: .first_frame) ?? ""
        pub_location = try container.decodeIfPresent(String.self, forKey: .pub_location) ?? ""
        cover43 = try container.decodeIfPresent(String.self, forKey: .cover43) ?? ""
        tidv2 = try container.decodeIfPresent(Int.self, forKey: .tidv2) ?? 0
        tnamev2 = try container.decodeIfPresent(String.self, forKey: .tnamev2) ?? ""
        pid_v2 = try container.decodeIfPresent(Int.self, forKey: .pid_v2) ?? 0
        pid_name_v2 = try container.decodeIfPresent(String.self, forKey: .pid_name_v2) ?? ""
        bvid = try container.decodeIfPresent(String.self, forKey: .bvid) ?? ""
        ogv_info = try container.decodeIfPresent(OgvInfo.self, forKey: .ogv_info)
        rcmd_reason = try container.decodeIfPresent(RcmdReason.self, forKey: .rcmd_reason)
//        up_tags = [] //try container.decodeIfPresent([String].self, forKey: .up_tags) ?? []
        score = try container.decodeIfPresent(Int.self, forKey: .score) ?? 0
        others = try container.decodeIfPresent([VideoItem].self, forKey: .others)
        enable_vt = try container.decodeIfPresent(Int.self, forKey: .enable_vt) ?? 0
        mission_id = try container.decodeIfPresent(Int.self, forKey: .mission_id)
        attribute = try container.decodeIfPresent(Int.self, forKey: .attribute)
        attribute_v2 = try container.decodeIfPresent(Int.self, forKey: .attribute_v2)
        season_id = try container.decodeIfPresent(Int.self, forKey: .season_id)
        dynamic = try container.decodeIfPresent(String.self, forKey: .dynamic) ?? ""
        cid = try container.decodeIfPresent(Int.self, forKey: .cid) ?? 0
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(aid, forKey: .aid)
        try container.encodeIfPresent(videos, forKey: .videos)
        try container.encode(tid, forKey: .tid)
        try container.encode(tname, forKey: .tname)
        try container.encode(copyright, forKey: .copyright)
        try container.encode(pic, forKey: .pic)
        try container.encode(title, forKey: .title)
        try container.encode(pubdate, forKey: .pubdate)
        try container.encode(ctime, forKey: .ctime)
        try container.encode(desc, forKey: .desc)
        try container.encode(state, forKey: .state)
        try container.encode(duration, forKey: .duration)
        try container.encode(rights, forKey: .rights)
        try container.encode(owner, forKey: .owner)
        try container.encode(stat, forKey: .stat)
        try container.encode(dimension, forKey: .dimension)
        try container.encode(short_link_v2, forKey: .short_link_v2)
        try container.encode(first_frame, forKey: .first_frame)
        try container.encode(pub_location, forKey: .pub_location)
        try container.encode(cover43, forKey: .cover43)
        try container.encode(tidv2, forKey: .tidv2)
        try container.encode(tnamev2, forKey: .tnamev2)
        try container.encode(pid_v2, forKey: .pid_v2)
        try container.encode(pid_name_v2, forKey: .pid_name_v2)
        try container.encode(bvid, forKey: .bvid)
        try container.encodeIfPresent(ogv_info, forKey: .ogv_info)
        try container.encodeIfPresent(rcmd_reason, forKey: .rcmd_reason)
//        try container.encode(up_tags, forKey: .up_tags)
        try container.encode(score, forKey: .score)
        try container.encodeIfPresent(others, forKey: .others)
        try container.encode(enable_vt, forKey: .enable_vt)
        try container.encodeIfPresent(mission_id, forKey: .mission_id)
        try container.encodeIfPresent(attribute, forKey: .attribute)
        try container.encodeIfPresent(attribute_v2, forKey: .attribute_v2)
        try container.encodeIfPresent(season_id, forKey: .season_id)
        try container.encode(dynamic, forKey: .dynamic)
        try container.encode(cid, forKey: .cid)
    }
}
